package com.tianji.learning.handler;

import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsBoardSeason;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.tianji.learning.constants.LearningContstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * @author Yandel
 * @date 2025/6/1 下午4:04
 * @description: 创建上赛季榜单
 */
@Component //找了很久没发现忘记加注解~
@RequiredArgsConstructor
@Slf4j
public class PointsBoardPersistentHandler {


    private final IPointsBoardSeasonService pointsBoardSeasonService;

    private final IPointsBoardService pointsBoardService;


    private final StringRedisTemplate redisTemplate;

    /**
     * 创建上赛季 榜单表
     */
//    @Scheduled(cron = "0 */2 * * * *") // 秒 分 时 日 月 周   测试用 //每两分钟执行一次
//       @Scheduled(cron = "0 0 3 ? * MON") 每周一凌晨 3 点执行
//     @Scheduled(cron = "0 0 3 1 * ?") //单机版 定时任务调度  每月1号，凌晨3点执行 单机版 定时任务调度
    @XxlJob("createTableJob")// 分布式版 定时任务调度
    public void createPointsBoardTableOfLastSeason() {
//        log.debug("创建上赛季榜单任务执行了");
        log.info("创建上赛季榜单任务执行了");
        // 获取上月当前时间点
        LocalDate time = LocalDate.now().minusMonths(1);
        // 查询赛季id
        PointsBoardSeason one = pointsBoardSeasonService
                .lambdaQuery()
                .le(PointsBoardSeason::getBeginTime, time)
                .ge(PointsBoardSeason::getEndTime, time)
                .one();
        log.info("上赛季信息 {}", one);
        if (one == null) {
            // 赛季不存在
            return;
        }
        log.info("赛季id {}", one.getId());
        // 创建表
        pointsBoardSeasonService.createPointsBoardLatestTable(one.getId());//基本每个赛季创建一次
        log.info("创建表成功");
    }

    /**
     * 测试
     */
/*    @XxlJob("learningXxlTest")
    public void test() {
        log.info("xxl-job测试~");
        System.out.println("xxl-job测试");

    }
    private final IPointsBoardSeasonService pointsBoardSeasonService;

    private final IPointsBoardService pointsBoardService;

    @Scheduled(cron = "0 0 3 1 * ?") // 每月1号，凌晨3点执行
    public void createPointsBoardTableOfLastSeason(){
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        // 2.查询赛季id
        Integer season = pointsBoardSeasonService.querySeasonByTime(time);
        if (season == null) {
            // 赛季不存在
            return;
        }
        // 3.创建表
        pointsBoardService.createPointsBoardTableBySeason(season);
    }*/


    /**
     * 持久化上赛季（上个月） 排行榜数据到DB中
     */
    @XxlJob("savePointsBoard2DB")
    public void savePointsBoard2DB() {
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);

        // 2.查询赛季信息
        PointsBoardSeason one = pointsBoardSeasonService
                .lambdaQuery()
                .le(PointsBoardSeason::getBeginTime, time)
                .ge(PointsBoardSeason::getEndTime, time)
                .one();
        log.info("上赛季信息 {}", one);
        if (one == null) {
            // 赛季不存在
            return;
        }

        // 3.计算动态表名 存放在ThreadLocal中
        String tableName = POINTS_BOARD_TABLE_PREFIX + one.getId();
        log.info("动态表名为 {}", tableName);
        TableInfoContext.setInfo(tableName);//存储在ThreadLocal中


        // 4.分页获取Redis中上个月赛季排行榜数据
        String format = time.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + format;//获取表名 boards:202507

        int shardIndex = XxlJobHelper.getShardIndex();//当前分片的索引  从0开始
        int shardTotal = XxlJobHelper.getShardTotal();//总分片数

        int pageNo = shardIndex + 1;//页码
        int pageSize = 100; // 增大页面大小以提高效率
        while (true) {
            log.info("处理第 {} 页数据", pageNo);
            List<PointsBoard> pointsBoardList = pointsBoardService.queryCurrentBoard(key, pageNo, pageSize);
            if (CollUtils.isEmpty(pointsBoardList)) {
                break;
            }
            pageNo+=shardTotal;
            // 5. 批量保存 持久化数据到数据库
            for (PointsBoard board : pointsBoardList) {
                board.setId(Long.valueOf(board.getRank()));//历史赛季排行榜中id  就代表了排名
                board.setRank(null);
            }
            pointsBoardService.saveBatch(pointsBoardList);
        }
        // 清空ThreadLocal中的表名
        TableInfoContext.remove();

    }

    /**
     * 清空Redis中的上赛季排行榜数据
     */
    @XxlJob("clearPointsBoardFromRedis")
    public void clearPointsBoardFromRedis() {
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        String format = time.format(DateTimeFormatter.ofPattern("yyyyMM"));//转成yyyyMM格式

        // 2.计算key
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + format;//获取表名 boards:202507

        // 3.删除
        // unlink是异步删除  delete是同步删除
        // delete不会释放分配的内存，而unlink会释放
        redisTemplate.unlink(key);//区别delete命令
    }

    /**
     * 严格按照顺序执行
     */
    @XxlJob("pointsBoardSeasonJob")
    public void handlePointsBoardSeason() {
        // 1.创建表
        createPointsBoardTableOfLastSeason();

        // 2.保存数据到数据库
        savePointsBoard2DB();

        // 3.清空 Redis
        clearPointsBoardFromRedis();
    }
}
